package FlexTrade;

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.ExportException;
import java.util.Hashtable;

import common_classes.IRemoteAccounts;

import net.jini.config.Configuration;
import net.jini.config.ConfigurationProvider;
import net.jini.core.transaction.UnknownTransactionException;
import net.jini.core.transaction.server.TransactionConstants;
import net.jini.core.transaction.server.TransactionManager;
import net.jini.core.transaction.server.TransactionParticipant;
import net.jini.export.Exporter;
import net.jini.jeri.BasicILFactory;
import net.jini.jeri.BasicJeriExporter;
import net.jini.jeri.tcp.TcpServerEndpoint;

/*
 * This is the remote bank service implementation
 */
public class RemoteAccountsImpl implements IRemoteAccounts, TransactionParticipant {
	/**
	 * 
	 */
	public static String CONFIG_FILE = "/home/hieuht/Eclipse/workspaceJEE/FlexTrade/src/FlexTrade/configFile.config";
	private static final long serialVersionUID = 1L;
	protected Hashtable<String, Float> customerAccounts;
	protected Hashtable<String, Float> providerAccounts;
	protected long crashCount = 0; // value??
	protected TransactionParticipant proxy;
	
	public RemoteAccountsImpl(){
		customerAccounts = new Hashtable<String, Float>();
		providerAccounts = new Hashtable<String, Float>();
		initializeBankAccounts();
		//export this object and get back proxy
		Exporter exporter = new BasicJeriExporter(
				TcpServerEndpoint.getInstance(0),
				new BasicILFactory());
		try {	// export an object of this class
			this.proxy = (TransactionParticipant)exporter.export(this);
		} catch (ExportException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void creditDebit(float amount, String customerID, String providerID,
			long transactionID, TransactionManager tm) throws RemoteException {
		// TODO Auto-generated method stub
		String[] configArgs = new String[] {CONFIG_FILE};
		try {
			// get the configuration (by default a FileConfiguration)
			Configuration config =  ConfigurationProvider.getInstance(configArgs);
			// and use this to construct an exporter
			Exporter exporter = (Exporter) ((net.jini.config.Configuration) config).getEntry(
			"JeriExport", "exporter", Exporter.class);
			// export an object of this class
			proxy = (TransactionParticipant) exporter.export(this);
		} catch(Exception e) {
			e.printStackTrace();
		}
		try {
			tm.join(transactionID, (TransactionParticipant) proxy, crashCount);
		} catch(Exception e) {
			e.printStackTrace();
		}
		float t_customerBalance = (Float) customerAccounts.get(customerID);
		if(t_customerBalance < amount){	//customer does not have enough money on account
			System.out.println("Customer does not have enough money in account to buy this product");
			try {		//abort this transaction
				tm.abort(transactionID);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {		//transfer money from customer account to provider account
			float customerAccountBalance = (Float) customerAccounts.remove(customerID);
			float providerAccountBalance = (Float) providerAccounts.remove(providerID);
			customerAccountBalance = customerAccountBalance - amount;
			providerAccountBalance = providerAccountBalance + amount;
			providerAccounts.put(providerID, providerAccountBalance);
			customerAccounts.put(customerID, customerAccountBalance);
			try {
				tm.commit(transactionID);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	
	private void initializeBankAccounts(){
		String customerKey;
		String providerKey;
		for(int i = 1; i<=4; i++){ 
			//initial balance of customer and provider is 5000E
			//the key in Hashtable is provider name/customer name
			customerKey = "customer" + Integer.toString(i);
			providerKey = "provider" + Integer.toString(i);
			customerAccounts.put(customerKey, (float)5000);
			providerAccounts.put(providerKey, (float)5000);
		}
	}
	
	public void setProxy(Remote _proxy) throws RemoteException {
		this.proxy = (TransactionParticipant) _proxy;
	}
	
	public void listAllBankAccounts() {
		String customerKey;
		String providerKey;
		System.out.println("/========== LIST OF BANK ACCOUNTS ==========================/");
		for(int i = 1; i<=4; i++){ 
			//initial balance of customer and provider is 5000E
			//the key in Hashtable is provider name/customer name
			customerKey = "customer" + Integer.toString(i);
			providerKey = "provider" + Integer.toString(i);
			float cus = (Float) customerAccounts.get(customerKey);
			float pro = (Float) providerAccounts.get(providerKey);
			System.out.println("Account: " + customerKey + ":" + Float.toString(cus) +  " | " + providerKey + ": " + Float.toString(pro));
		}
		System.out.println("/========== END OF BANK ACCOUNTS ==========================/");
	}
	
	@Override
	public void abort(TransactionManager arg0, long arg1)
			throws UnknownTransactionException, RemoteException {
		// TODO Auto-generated method stub
		System.out.println("Remote Account is aborted");
		listAllBankAccounts();
	}

	@Override
	public void commit(TransactionManager arg0, long arg1)
			throws UnknownTransactionException, RemoteException {
		// TODO Auto-generated method stub
		System.out.println("Remote Account is committed");
		listAllBankAccounts();
	}

	@Override
	public int prepare(TransactionManager arg0, long arg1)
			throws UnknownTransactionException, RemoteException {
		// TODO Auto-generated method stub
		System.out.println("Remote Account is prepared");
		return TransactionConstants.PREPARED;
	}

	@Override
	public int prepareAndCommit(TransactionManager arg0, long arg1)
			throws UnknownTransactionException, RemoteException {
		// TODO Auto-generated method stub
		int result = prepare(arg0, arg1);
		if (result == TransactionConstants.PREPARED) {
		commit(arg0, arg1);
		result = TransactionConstants.COMMITTED;
		}
		listAllBankAccounts();
		return result;
	}
}
